#include "usart1.h"
#include "sys.h"
#include "led.h"
#include "dac1.h"
#include "curtain.h"
#include "help_curtain.h"
#include "volleyball.h"

/* 暂时未用到的变量
排球中心点,注意发射板和接收板位置不一样,不要将两根放在一起直接贴标签
纵向：第24点 uint8_t center_x = 16;
横向：第16点 uint8_t center_y = 24;
*/
const uint16_t default_scan_list_volleyball[5] = {0xFFFF, 0xFFFF, 0xFFFF, 0x07FF, 0xFFE0};
const uint8_t valid_area[48][2] = {
    {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {17, 15}, {20, 12}, {22, 10}, {23, 9}, {24, 8}, {25, 7}, {25, 7}, {26, 6}, {26, 6}, {26, 6}, {26, 6}, {26, 6}, {26, 6}, {26, 6}, {25, 7}, {25, 7}, {24, 8}, {23, 9}, {22, 10}, {20, 12}, {17, 15}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}};

void GetFirstAndLastBit1(uint16_t n, uint8_t *pHigh, uint8_t *pLow)
{
    *pHigh = *pLow = 0;

    if (n == 0)
    {
        return;
    }

    for (uint8_t i = 0; i < 16; i++, n >>= 1)
    {
        if (n & 0x01)
        {
            if (*pLow == 0)
            {
                *pLow = i + 1;
            }
            *pHigh = i + 1;
        }
    }
}

// 检查是否打在了有效部分
static uint8_t VolleyballGetHitAreaIsValid(void)
{
    // 返回值 0xFF    没有球打在板上任何部位  0x01    有球击打在了板上有效部位  0x00    有求击中，但打在了无效部位
    uint8_t i, j, top = 0, bottom = 0, left = 0, right = 0;
    uint16_t scan_result[5] = {0};
    uint8_t h[5], l[5];
    uint8_t ret_value;
    float mid;

    // 扫描三组红外线，每组16个
    for (i = 0; i < 3; i++)
    {
        scan_result[i] = curtain_read_light_status(i + 1, default_scan_list_volleyball[i]);
        Delay_us(5);
    }
    // 还未做任何检查，认为没有球击中
    ret_value = 0xFF;

    // 纵向区域保存在scan_result[0],[1],[2], 横向区域保存在scan_result[3],[4]
    if ((scan_result[0] != 0) || (scan_result[1] != 0) || (scan_result[2] != 0))
    {
        // 暂时认为打在了无效部位
        ret_value = 0x00;
        // 扫描二组红外线，每组16个,扫描j次
        j = 10;
        scan_result[3] = scan_result[4] = 0x00;
        while (j--)
        {
            for (i = 3; i < 5; i++)
            {
                scan_result[i] |= curtain_read_light_status(i + 1, default_scan_list_volleyball[i]);
                Delay_us(5); // 延时5us，避免从机在主机端的反射信号和主机信号在从机处叠加，触发TVS
            }
        }
        if ((scan_result[3] != 0) || (scan_result[4] != 0))
        {
            // 接下来验证是不是有效部分 分别找出每块板最高位和最低位
            for (i = 0; i < 5; i++)
            {
                GetFirstAndLastBit1(scan_result[i], &h[i], &l[i]);
            }
            // 算出最上和最下位置
            top = bottom = left = right = 0;
            for (i = 0; i < 3; i++)
            {
                if ((l[i] != 0) && (bottom == 0))
                {
                    bottom = l[i] + i * 16;
                }

                if (h[i] != 0)
                {
                    top = h[i] + i * 16;
                }
            }
            // 算出最左和最右的位置
            if (h[4] != 0)
            {
                left = h[4] + 16;
            }
            else
            {
                left = h[3];
            }
            if (l[3] != 0)
            {
                right = l[3];
            }
            else
            {
                right = l[4] + 16;
            }
            // 判断是否在有效区域内，中等难度
            for (i = bottom - 1; i < top; i++)
            {
                mid = ((float)right + (float)left) / 2;
                // 击中的点中的中点在框之内,在边界上不算则认为击中了
                if ((mid < valid_area[i][0]) && (mid > valid_area[i][1]))
                {
                    return 0x01;
                }
            }
        }
    }
    return ret_value;
}

// 排球主机
void Volleyball(void)
{
    uint16_t scan_count = 0;    // 用于记录多长时间没有击球记录，用以判断是否掉球
    uint8_t hit_result = 0;     // 记录探测的击球结果
    uint8_t hit = 0, total = 0; // 记录击中次数和总次数
    if (curtains_temp_read_scanlist(5))
    {
        led_display_infomation_and_wait_once((uint8_t *)str_volleyball_02);
    }
    static_display_mode();             // 静态显示模式
    Delay_ms(1);                       // 等待转换完成
    led_update_volleyball_score(0, 0); // 显示 00:00
    while (1)
    {
        hit_result = VolleyballGetHitAreaIsValid();
        // 记录扫描次数，有太多次没有扫描到球，认为掉球了
        scan_count++;
        // 有效球
        if (hit_result == 0x01)
        {
            hit++;
            total++;
            scan_count = 0;
            // 有效球声音
            dac1_play_n_ms_by_index(SND_783HZ, 100, 1);
            led_update_volleyball_score(hit, total);
            Delay_ms(500);
        }
        // 无效球
        else if (hit_result == 0x00)
        {
            total++;
            scan_count = 0;
            // 无效球声音
            dac1_play_n_ms_by_index(SND_783HZ, 100, 1);
            led_update_volleyball_score(hit, total);
            Delay_ms(500);
        }

        // 击球超时，掉球了,或一直空闲
        if (scan_count > 500)
        {
            if (total != 0)
            {
                dac1_play_n_ms_by_index(SND_783HZ, 100, 1);
                Delay_ms(50);
                dac1_play_n_ms_by_index(SND_783HZ, 100, 1);
                Delay_ms(500);
                dac1_play_digit(hit);
                Delay_ms(500);
            }

            scan_count = 0;
            hit = 0;
            total = 0;
        }

        if (total == 40)
        {
            dac1_play_by_index(SND_783HZ, 1);
            Delay_ms(500);
            dac1_play_digit(hit);
            Delay_ms(500);
            scan_count = 0;
            hit = 0;
            total = 0;
        }
    }
}

void poweron_sequence_for_volleybal()
{
    led_display_infomation_and_wait_once((uint8_t *)str_volleyball_01); // 显示仪器名称 排辅测试仪
    dac1_play_by_index(SND_440HZ, 1);                                   // 提示音
    usart1_init_with_RXNE(BAUD_1M);                                     // 1M通讯
    switch (curtain_online_count(5))
    {
    case 0:                                                                                             // 全不在线
        curtain_test_and_block_some_light(1, 5, 10000, 1700, (uint16_t *)default_scan_list_volleyball); // 测试光线情况
        if (curtain_online_count(5) == 10)
        {
            Volleyball(); // 进入测试
        }
        else
        {
            curtains_485_test(5);
        }
        break;

    case 10:          // 全在线
        Volleyball(); // 排球测试程序
        break;

    default:                                                                                       // 部分在线，实时显示
        curtains_light_status_display(6, (uint16_t *)default_scan_list_volleyball, 1000, 2000, 0); // 实时显示
        break;
    }
}